#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "circular_buf.h"

#define min(x,y)    ((x) < (y) ? (x) : (y))


int initCircularBuf(struct circularBuffer *buf, unsigned int size)
{
	buf->m_size = size;
	buf->m_in = buf->m_out = 0;

	buf->m_buffer = (unsigned char *)malloc(buf->m_size);
	if(buf->m_buffer == NULL)
	{
		printf("circular buffer malloc failed!\n");
		return -1;
	}

	memset(buf->m_buffer, 0, buf->m_size);
	return 0;
}

void freeCircularBuf(struct circularBuffer *buf)
{
	if(buf != NULL)
	{
        buf->m_size = 0;
        buf->m_in = buf->m_out = 0;
		
		if(buf->m_buffer != NULL)
		{
       		free(buf->m_buffer);
			buf->m_buffer = NULL;
		}
	}
}

void clearCircularBuf(struct circularBuffer *buf)
{
	if(buf == NULL)
	{
		return;
	}

	if(buf->m_buffer == NULL)
	{
		return;
	}

	std::unique_lock<std::mutex> ulock(buf->m_lock);
	buf->m_in=buf->m_out=0;
	ulock.unlock();
}

unsigned int getDataCount(struct circularBuffer *buf)
{
	if(buf == NULL)
	{
		return 0;
	}

	if(buf->m_buffer == NULL)
	{
		return 0;
	}

    return (buf->m_in - buf->m_out);
}

unsigned int getFreeCount(struct circularBuffer *buf)
{
	if(buf == NULL)
	{
		return 0;
	}

	if(buf->m_buffer == NULL)
	{
		return 0;
	}

    return (buf->m_size - (buf->m_in - buf->m_out));
}

unsigned int readCircularBuf(struct circularBuffer *buf, unsigned char *pdata, unsigned int len)
{
	unsigned int l;
	unsigned int mask;
	unsigned int count = len;

	if(buf == NULL)
	{
		return 0;
	}

	if(buf->m_buffer == NULL)
	{
		return 0;
	}

	std::unique_lock<std::mutex> ulock(buf->m_lock);

	mask =buf->m_size-1;

	count	= min(count, buf->m_in - buf->m_out);
	l	= min(count, buf->m_size - (buf->m_out &mask));

	memcpy(pdata, buf->m_buffer+ (buf->m_out & mask), l);
	memcpy(pdata+l, buf->m_buffer, count -l);

	buf->m_out+=count;

	ulock.unlock();

	return count;
}

unsigned int writeCircularBuf(struct circularBuffer *buf, unsigned char *pdata, unsigned int len)
{
	unsigned int l;
	unsigned int mask;
	unsigned int count = len;

	if(buf == NULL)
	{
		return 0;
	}

	if(buf->m_buffer == NULL)
	{
		return 0;
	}

	std::unique_lock<std::mutex> ulock(buf->m_lock);

	mask = buf->m_size - 1;

	count = min(count, mask & (mask - buf->m_in + buf->m_out));

	l = min(count, buf->m_size - (buf->m_in & mask));

    memcpy(buf->m_buffer + (buf->m_in & mask), pdata, l);
	memcpy(buf->m_buffer, pdata+l, count - l);

	buf->m_in += count;

	ulock.unlock();
	return count;
}
